/** ----------------------------------------------------------
 * \class VSShaderLib
 *
 * Lighthouse3D
 *
 * VSShaderLib - Very Simple Shader Library
 *
 * Full documentation at 
 * http://www.lighthouse3d.com/very-simple-libs
 *
 * This class aims at making life simpler
 * when using shaders and uniforms
 *
 * \version 0.2.2
 *		Added the possibility ot use instance names in a
 *			uniform block
 * \version 0.2.1
 *		Added more attrib defs, namely
 *			tangents, bi tangents, and 4 custom
 * 
 * version 0.2.0
 *		Added methods to set uniforms
 *		Added methods to set blocks
 *		Renamed to VSShaderLib
 *
 * version 0.1.0 
 * Initial Release
 *
 * This lib requires:
 *
 * GLEW (http://glew.sourceforge.net/)
 *
 ---------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>

#include "vsShaderLib.h"


// pre conditions are established with asserts
// if having errors using the lib switch to Debug mode
#include <assert.h>

#ifdef __ANDROID_API__
#include <android/log.h>
AAssetManager *VSShaderLib::s_AssetManager = NULL;

void
VSShaderLib::SetAssetManager(AAssetManager *mgr) {
	s_AssetManager = mgr;
}
#endif

GLenum 
VSShaderLib::spGLShaderTypes[VSShaderLib::COUNT_SHADER_TYPE] = {
								GL_VERTEX_SHADER,
#ifndef __ANDROID_API__
								GL_GEOMETRY_SHADER,
								GL_TESS_CONTROL_SHADER,
								GL_TESS_EVALUATION_SHADER,
#endif
								GL_FRAGMENT_SHADER,
								GL_COMPUTE_SHADER};


std::string 
VSShaderLib::spStringShaderTypes[VSShaderLib::COUNT_SHADER_TYPE] = {
								"Vertex Shader",
#ifndef __ANDROID_API__
								"Geometry Shader",
								"Tesselation Control Shader",
								"Tesselation Evaluation Shader",
#endif
								"Fragment Shader",
								"Compute Shader"};


std::map<std::string, VSShaderLib::UniformBlock> VSShaderLib::spBlocks;

unsigned int VSShaderLib::spBlockCount = 1;


VSShaderLib::VSShaderLib(): pProgram(0), pInited(false) {

	for (int i = 0; i < VSShaderLib::COUNT_SHADER_TYPE; ++i) {
		pShader[i] = 0;
	}
}


VSShaderLib::~VSShaderLib() {

	if (pProgram)
		glDeleteProgram(pProgram);

	for (int i = 0; i < VSShaderLib::COUNT_SHADER_TYPE; ++i) {
		if (pShader[i])
			glDeleteShader(pShader[i]);
	}
	pUniforms.clear();
}


void 
VSShaderLib::init() {

	pInited = true;
	pProgram = glCreateProgram();
}


void 
VSShaderLib::loadShader(VSShaderLib::ShaderType st, std::string fileName) {

	// init should always be called first
	assert(pInited);

	char *s = NULL;

	s = textFileRead(fileName);

	if (s != NULL) {
		const char * ss = s;

		pShader[st] = glCreateShader(spGLShaderTypes[st]);
		glShaderSource(pShader[st], 1, &ss,NULL);
		glAttachShader(pProgram, pShader[st]);
		glCompileShader(pShader[st]);

		free(s);
	}
}


void
VSShaderLib::prepareProgram() {

	glLinkProgram(pProgram);
	addUniforms();
	addBlocks();
}


#ifndef __ANDROID_API__
void 
VSShaderLib::setProgramOutput(int index, std::string name) {

	glBindFragDataLocation(pProgram, index, name.c_str());
}
#endif


GLint
VSShaderLib::getProgramOutput(std::string name) {

	return glGetFragDataLocation(pProgram, name.c_str());
}


void
VSShaderLib::setVertexAttribName(VSShaderLib::AttribType at, std::string name) {

	glBindAttribLocation(pProgram,at,name.c_str());
}


GLuint
VSShaderLib::getProgramIndex() {

	return pProgram;
}


GLuint
VSShaderLib::getShaderIndex(VSShaderLib::ShaderType aType) {

	return pShader[aType];
}


void 
VSShaderLib::setBlock(std::string name, void *value) {

	if (spBlocks.count(name) != 0) {

		glBindBuffer(GL_UNIFORM_BUFFER, spBlocks[name].buffer);
		glBufferSubData(GL_UNIFORM_BUFFER, 0, spBlocks[name].size, value);
		glBindBuffer(GL_UNIFORM_BUFFER,0);
	}
}


void 
VSShaderLib::setBlockUniform(std::string blockName, 
						std::string uniformName, 
						void *value) {

	if (!spBlocks.count(blockName))
		return;

	std::string uniformComposed = blockName + "." + uniformName;
	std::string finalUniName;

	if (spBlocks[blockName].uniformOffsets.count(uniformName))
		finalUniName = uniformName;
	else if (spBlocks[blockName].uniformOffsets.count(uniformComposed))
		finalUniName = uniformComposed;
	else
		return;

	UniformBlock b;
	b = spBlocks[blockName];

	myBlockUniform bUni;
	bUni = b.uniformOffsets[finalUniName];
	glBindBuffer(GL_UNIFORM_BUFFER, b.buffer);
	glBufferSubData(GL_UNIFORM_BUFFER, bUni.offset, bUni.size, value);
	glBindBuffer(GL_UNIFORM_BUFFER,0);
}


void 
VSShaderLib::setBlockUniformArrayElement(std::string blockName, 
								std::string uniformName,
								int arrayIndex, 
								void * value) {

	assert(spBlocks.count(blockName) && 
		   spBlocks[blockName].uniformOffsets.count(uniformName));

	UniformBlock b;
	b = spBlocks[blockName];

	myBlockUniform bUni;
	bUni = b.uniformOffsets[uniformName];

	glBindBuffer(GL_UNIFORM_BUFFER, b.buffer);
	glBufferSubData(GL_UNIFORM_BUFFER, 
						bUni.offset + bUni.arrayStride * arrayIndex, 
						bUni.arrayStride, value);
	glBindBuffer(GL_UNIFORM_BUFFER,0);
}


void 
VSShaderLib::setUniform(std::string name, int value) {

	int val = value;
	myUniforms u = pUniforms[name];
	glProgramUniform1i(pProgram, u.location, val);

}


void 
VSShaderLib::setUniform(std::string name, float value) {

	float val = value;
	myUniforms u = pUniforms[name];
	glProgramUniform1f(pProgram, u.location, val);
}


void 
VSShaderLib::setUniform(std::string name, void *value) {

	myUniforms u = pUniforms[name];
	switch (u.type) {
	
		// Floats
		case GL_FLOAT: 
			glProgramUniform1fv(pProgram, u.location, u.size, (const GLfloat *)value);
			break;
		case GL_FLOAT_VEC2:  
			glProgramUniform2fv(pProgram, u.location, u.size, (const GLfloat *)value);
			break;
		case GL_FLOAT_VEC3:  
			glProgramUniform3fv(pProgram, u.location, u.size, (const GLfloat *)value);
			break;
		case GL_FLOAT_VEC4:  
			glProgramUniform4fv(pProgram, u.location, u.size, (const GLfloat *)value);
			break;
#ifndef __ANDROID_API__
		// Doubles
		case GL_DOUBLE: 
			glProgramUniform1dv(pProgram, u.location, u.size, (const GLdouble *)value);
			break;
		case GL_DOUBLE_VEC2:  
			glProgramUniform2dv(pProgram, u.location, u.size, (const GLdouble *)value);
			break;
		case GL_DOUBLE_VEC3:  
			glProgramUniform3dv(pProgram, u.location, u.size, (const GLdouble *)value);
			break;
		case GL_DOUBLE_VEC4:  
			glProgramUniform4dv(pProgram, u.location, u.size, (const GLdouble *)value);
			break;
#endif
		// Samplers, Ints and Bools
#ifndef __ANDROID_API__
		case GL_IMAGE_1D :
		case GL_IMAGE_2D_RECT :
		case GL_IMAGE_BUFFER :
		case GL_IMAGE_1D_ARRAY :
		case GL_IMAGE_CUBE_MAP_ARRAY :
		case GL_IMAGE_2D_MULTISAMPLE :
		case GL_IMAGE_2D_MULTISAMPLE_ARRAY :
		case GL_INT_IMAGE_1D :
		case GL_INT_IMAGE_2D_RECT :
		case GL_INT_IMAGE_BUFFER :
		case GL_INT_IMAGE_1D_ARRAY :
		case GL_INT_IMAGE_CUBE_MAP_ARRAY :
		case GL_INT_IMAGE_2D_MULTISAMPLE :
		case GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY :
		case GL_UNSIGNED_INT_IMAGE_1D :
		case GL_UNSIGNED_INT_IMAGE_2D_RECT :
		case GL_UNSIGNED_INT_IMAGE_BUFFER :
		case GL_UNSIGNED_INT_IMAGE_1D_ARRAY :
		case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY :
		case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE :
		case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY :
#endif
		case GL_IMAGE_2D :
		case GL_IMAGE_3D :
		case GL_IMAGE_CUBE :
		case GL_IMAGE_2D_ARRAY :
		case GL_INT_IMAGE_2D :
		case GL_INT_IMAGE_3D :
		case GL_INT_IMAGE_CUBE :
		case GL_INT_IMAGE_2D_ARRAY :
		case GL_UNSIGNED_INT_IMAGE_2D :
		case GL_UNSIGNED_INT_IMAGE_3D :
		case GL_UNSIGNED_INT_IMAGE_CUBE :
		case GL_UNSIGNED_INT_IMAGE_2D_ARRAY :

#ifndef __ANDROID_API__
		case GL_SAMPLER_1D:
		case GL_SAMPLER_1D_SHADOW:
		case GL_SAMPLER_1D_ARRAY:
		case GL_SAMPLER_1D_ARRAY_SHADOW:
		case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
		case GL_SAMPLER_BUFFER:
		case GL_SAMPLER_2D_RECT:
		case GL_SAMPLER_2D_RECT_SHADOW:
		case GL_INT_SAMPLER_1D:
		case GL_INT_SAMPLER_1D_ARRAY:
		case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
		case GL_INT_SAMPLER_BUFFER:
		case GL_INT_SAMPLER_2D_RECT:
		case GL_UNSIGNED_INT_SAMPLER_1D:
		case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
		case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
		case GL_UNSIGNED_INT_SAMPLER_BUFFER:
		case GL_UNSIGNED_INT_SAMPLER_2D_RECT:
#endif
		case GL_SAMPLER_2D:
		case GL_SAMPLER_3D:
		case GL_SAMPLER_CUBE:
		case GL_SAMPLER_2D_SHADOW:
		case GL_SAMPLER_2D_ARRAY:
		case GL_SAMPLER_2D_ARRAY_SHADOW:
		case GL_SAMPLER_2D_MULTISAMPLE:
		case GL_SAMPLER_CUBE_SHADOW:
		case GL_INT_SAMPLER_2D:
		case GL_INT_SAMPLER_3D:
		case GL_INT_SAMPLER_CUBE:
		case GL_INT_SAMPLER_2D_ARRAY:
		case GL_INT_SAMPLER_2D_MULTISAMPLE:
		case GL_UNSIGNED_INT_SAMPLER_2D:
		case GL_UNSIGNED_INT_SAMPLER_3D:
		case GL_UNSIGNED_INT_SAMPLER_CUBE:
		case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
		case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
		case GL_BOOL:
		case GL_INT : 
			glProgramUniform1iv(pProgram, u.location, u.size, (const GLint *)value);
			break;
		case GL_BOOL_VEC2:
		case GL_INT_VEC2:  
			glProgramUniform2iv(pProgram, u.location, u.size, (const GLint *)value);
			break;
		case GL_BOOL_VEC3:
		case GL_INT_VEC3:  
			glProgramUniform3iv(pProgram, u.location, u.size, (const GLint *)value);
			break;
		case GL_BOOL_VEC4:
		case GL_INT_VEC4:  
			glProgramUniform4iv(pProgram, u.location, u.size, (const GLint *)value);
			break;

		// Unsigned ints
		case GL_UNSIGNED_INT: 
			glProgramUniform1uiv(pProgram, u.location, u.size, (const GLuint *)value);
			break;
		case GL_UNSIGNED_INT_VEC2:  
			glProgramUniform2uiv(pProgram, u.location, u.size, (const GLuint *)value);
			break;
		case GL_UNSIGNED_INT_VEC3:  
			glProgramUniform3uiv(pProgram, u.location, u.size, (const GLuint *)value);
			break;
		case GL_UNSIGNED_INT_VEC4:  
			glProgramUniform4uiv(pProgram, u.location, u.size, (const GLuint *)value);
			break;

		// Float Matrices
		case GL_FLOAT_MAT2:
			glProgramUniformMatrix2fv(pProgram, u.location, u.size, GL_FALSE, (const GLfloat *)value);
			break;
		case GL_FLOAT_MAT3:
			glProgramUniformMatrix3fv(pProgram, u.location, u.size, GL_FALSE, (const GLfloat *)value);
			break;
		case GL_FLOAT_MAT4:
			glProgramUniformMatrix4fv(pProgram, u.location, u.size, GL_FALSE, (const GLfloat *)value);
			break;
		case GL_FLOAT_MAT2x3:
			glProgramUniformMatrix2x3fv(pProgram, u.location, u.size, GL_FALSE, (const GLfloat *)value);
			break;
		case GL_FLOAT_MAT2x4:
			glProgramUniformMatrix2x4fv(pProgram, u.location, u.size, GL_FALSE, (const GLfloat *)value);
			break;
		case GL_FLOAT_MAT3x2:
			glProgramUniformMatrix3x2fv(pProgram, u.location, u.size, GL_FALSE, (const GLfloat *)value);
			break;
		case GL_FLOAT_MAT3x4:
			glProgramUniformMatrix3x4fv(pProgram, u.location, u.size, GL_FALSE, (const GLfloat *)value);
			break;
		case GL_FLOAT_MAT4x2:
			glProgramUniformMatrix4x2fv(pProgram, u.location, u.size, GL_FALSE, (const GLfloat *)value);
			break;
		case GL_FLOAT_MAT4x3:
			glProgramUniformMatrix4x3fv(pProgram, u.location, u.size, GL_FALSE, (const GLfloat *)value);
			break;

#ifndef __ANDROID_API__
        // Double Matrices
		case GL_DOUBLE_MAT2:
			glProgramUniformMatrix2dv(pProgram, u.location, u.size, false, (const GLdouble *)value);
			break;
		case GL_DOUBLE_MAT3:
			glProgramUniformMatrix3dv(pProgram, u.location, u.size, false, (const GLdouble *)value);
			break;
		case GL_DOUBLE_MAT4:
			glProgramUniformMatrix4dv(pProgram, u.location, u.size, false, (const GLdouble *)value);
			break;
		case GL_DOUBLE_MAT2x3:
			glProgramUniformMatrix2x3dv(pProgram, u.location, u.size, false, (const GLdouble *)value);
			break;
		case GL_DOUBLE_MAT2x4:
			glProgramUniformMatrix2x4dv(pProgram, u.location, u.size, false, (const GLdouble *)value);
			break;
		case GL_DOUBLE_MAT3x2:
			glProgramUniformMatrix3x2dv(pProgram, u.location, u.size, false, (const GLdouble *)value);
			break;
		case GL_DOUBLE_MAT3x4:
			glProgramUniformMatrix3x4dv(pProgram, u.location, u.size, false, (const GLdouble *)value);
			break;
		case GL_DOUBLE_MAT4x2:
			glProgramUniformMatrix4x2dv(pProgram, u.location, u.size, false, (const GLdouble *)value);
			break;
		case GL_DOUBLE_MAT4x3:
			glProgramUniformMatrix4x3dv(pProgram, u.location, u.size, false, (const GLdouble *)value);
			break;
#endif
	}
}


std::string
VSShaderLib::getShaderInfoLog(VSShaderLib::ShaderType st) {

    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;

	pResult = "";

	if (pShader[st]) {
		glGetShaderiv(pShader[st], GL_INFO_LOG_LENGTH,&infologLength);

		if (infologLength > 0)
		{
			infoLog = (char *)malloc((size_t)infologLength);
			glGetShaderInfoLog(pShader[st], infologLength, &charsWritten, infoLog);
			if (charsWritten)
				pResult = infoLog;
			else
				pResult= "OK";
			free(infoLog);
		}
		else
			pResult="OK";
	}
	else
		pResult = "Shader not loaded";
	return pResult;
}


std::string
VSShaderLib::getProgramInfoLog() {

    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;

	pResult = "";

	if (pProgram) {

		glGetProgramiv(pProgram, GL_INFO_LOG_LENGTH,&infologLength);

		if (infologLength > 0)
		{
			infoLog = (char *)malloc((size_t)infologLength);
			glGetProgramInfoLog(pProgram, infologLength, &charsWritten, infoLog);
			pResult = infoLog;
			if (charsWritten)
				pResult = infoLog;
			else
				pResult= "OK";
			free(infoLog);
		}
	}
	return pResult;
}


bool
VSShaderLib::isProgramValid() {

	GLint b = GL_FALSE;

	if (pProgram) {
	
		glValidateProgram(pProgram);
		glGetProgramiv(pProgram, GL_VALIDATE_STATUS,&b);
	}

	return (b != GL_FALSE);
}


bool
VSShaderLib::isShaderCompiled(VSShaderLib::ShaderType aType) {

	GLint b = GL_FALSE;

	if (pShader[aType]) {
	
		glGetShaderiv(pShader[aType], GL_INFO_LOG_LENGTH, &b);
	}

	return (b != GL_FALSE);
}


bool
VSShaderLib::isProgramLinked() {

	GLint b = GL_FALSE;

	if (pProgram) {
	
		glGetProgramiv(pProgram, GL_LINK_STATUS, &b);
	}

	return (b != GL_FALSE);
}


std::string 
VSShaderLib::getAllInfoLogs() {

	std::string s;

	for (int i = 0; i < VSShaderLib::COUNT_SHADER_TYPE; ++i) {
		if (pShader[i]) {
			getShaderInfoLog((VSShaderLib::ShaderType)i);
			s.append(VSShaderLib::spStringShaderTypes[i]);
			s.append(": ");
			s.append(pResult);
			s.append("\n");
			//s += VSShaderLib::spStringShaderTypes[i] + ": " + pResult + "\n";
		}
	}

	if (pProgram) {
		getProgramInfoLog();
		s += "Program: " + pResult;
		if (isProgramValid())
			s += " - Valid\n";
		else
			s += " - Not Valid\n";
	}

	pResult = s;
	return pResult;
}


// PRIVATE METHODS

char *
VSShaderLib::textFileRead(std::string fileName) {

	char *content = NULL;

#ifndef __ANDROID_API__
	FILE *fp;

	size_t count=0;

	if (fileName != "") {
		fp = fopen(fileName.c_str(),"rt");

		if (fp != NULL) {
      
			fseek(fp, 0, SEEK_END);
			count = ftell(fp);
			rewind(fp);

			if (count > 0) {
				content = (char *)malloc(sizeof(char) * (count+1));
				count = fread(content, sizeof(char), count, fp);
				content[count] = '\0';
			}
			fclose(fp);
		}
	}
	return content;
#else
	AAsset* asset = AAssetManager_open(s_AssetManager, fileName.c_str(), AASSET_MODE_UNKNOWN);
	const char *loc = "VSShaderLib::readText";
	if (NULL == asset) {
		__android_log_print(ANDROID_LOG_ERROR, loc, "%s", "_ASSET_NOT_FOUND_");
		return content;
	}
	size_t size = (size_t)AAsset_getLength(asset);
	content = (char*)malloc(sizeof(char)*size + 1);
	AAsset_read(asset, content, size);
	content[size] = '\0';
	__android_log_print(ANDROID_LOG_DEBUG, loc, "%s", content);
	AAsset_close(asset);
	return content;
#endif
}


void
VSShaderLib::addBlocks() {

	int count, dataSize, actualLen, activeUnif, maxUniLength;
	int uniType, uniSize, uniOffset, uniMatStride, uniArrayStride, auxSize;
	char *name, *name2;

	glGetProgramiv(pProgram, GL_ACTIVE_UNIFORM_BLOCKS, &count);

	for (int i = 0; i < count; ++i) {
		// Get buffers name
		UniformBlock block;
		glGetActiveUniformBlockiv(pProgram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &actualLen);
		name = (char *)malloc(sizeof(char) * actualLen);
		glGetActiveUniformBlockName(pProgram, i, actualLen, NULL, name);

		bool newBlock=true;
		if (spBlocks.count(name)) {
			newBlock = false;
			block = spBlocks[name];
		}

		glGetActiveUniformBlockiv(pProgram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &dataSize);

		if (newBlock) {
			glGenBuffers(1, &block.buffer);
			glBindBuffer(GL_UNIFORM_BUFFER, block.buffer);
			glBufferData(GL_UNIFORM_BUFFER, dataSize, NULL, GL_DYNAMIC_DRAW);
			glUniformBlockBinding(pProgram, i, spBlockCount);
			glBindBufferRange(GL_UNIFORM_BUFFER, spBlockCount, block.buffer, 0, dataSize);
		}
		else {
			glBindBuffer(GL_UNIFORM_BUFFER, block.buffer);
			glUniformBlockBinding(pProgram, i, block.bindingIndex);
		}
		glGetActiveUniformBlockiv(pProgram, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &activeUnif);

		unsigned int *indices;
		indices = (unsigned int *)malloc(sizeof(unsigned int) * activeUnif);
		glGetActiveUniformBlockiv(pProgram, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, (int *)indices);
			
		glGetProgramiv(pProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniLength);
		name2 = (char *)malloc(sizeof(char) * maxUniLength);

		for (int k = 0; k < activeUnif; ++k) {
		
			myBlockUniform bUni;

			GLenum type;

			glGetActiveUniform(pProgram, indices[k], maxUniLength, &actualLen, &uniSize, &type, name2);

			glGetActiveUniformsiv(pProgram, 1, &indices[k], GL_UNIFORM_TYPE, &uniType);
			glGetActiveUniformsiv(pProgram, 1, &indices[k], GL_UNIFORM_SIZE, &uniSize);
			glGetActiveUniformsiv(pProgram, 1, &indices[k], GL_UNIFORM_OFFSET, &uniOffset);
			glGetActiveUniformsiv(pProgram, 1, &indices[k], GL_UNIFORM_MATRIX_STRIDE, &uniMatStride);
			glGetActiveUniformsiv(pProgram, 1, &indices[k], GL_UNIFORM_ARRAY_STRIDE, &uniArrayStride);
			
			if (uniArrayStride > 0)
				auxSize = uniArrayStride * uniSize;
				
			else if (uniMatStride > 0) {

				switch (uniType) {
				case GL_FLOAT_MAT2:
				case GL_FLOAT_MAT2x3:
				case GL_FLOAT_MAT2x4:
#ifndef __ANDROID_API__
				case GL_DOUBLE_MAT2:
				case GL_DOUBLE_MAT2x3:
				case GL_DOUBLE_MAT2x4:
					auxSize = 2 * uniMatStride;
					break;
#endif
				case GL_FLOAT_MAT3:
				case GL_FLOAT_MAT3x2:
				case GL_FLOAT_MAT3x4:
#ifndef __ANDROID_API__
				case GL_DOUBLE_MAT3:
				case GL_DOUBLE_MAT3x2:
				case GL_DOUBLE_MAT3x4:
#endif
					auxSize = 3 * uniMatStride;
					break;
				case GL_FLOAT_MAT4:
				case GL_FLOAT_MAT4x2:
				case GL_FLOAT_MAT4x3:
#ifndef __ANDROID_API__
				case GL_DOUBLE_MAT4:
				case GL_DOUBLE_MAT4x2:
				case GL_DOUBLE_MAT4x3:
#endif
					auxSize = 4 * uniMatStride;
					break;
				}
			}
			else
				auxSize = typeSize(uniType);

			bUni.offset = uniOffset;
			bUni.type = uniType;
			bUni.size = auxSize;
			bUni.arrayStride = uniArrayStride;

			block.uniformOffsets[name2] = bUni;


		}
		free(name2);
		if (newBlock) {
		block.size = dataSize;
		block.bindingIndex = spBlockCount;
		spBlockCount++;
		}
		spBlocks[name] = block;
	}
}


void
VSShaderLib::addUniforms() {

	int count;
	GLsizei actualLen;
	GLint size;
	GLint uniArrayStride;
	GLenum type;
	char *name;

	int maxUniLength;
	glGetProgramiv(pProgram, GL_ACTIVE_UNIFORMS, &count);

	glGetProgramiv(pProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniLength);

	name = (char *)malloc(sizeof(char) * maxUniLength);

	unsigned int loc;
	for (int i = 0; i < count; ++i) {

		glGetActiveUniform(pProgram, i, maxUniLength, &actualLen, &size, &type, name);
		// -1 indicates that is not an active uniform, although it may be present in a
		// uniform block
		loc = glGetUniformLocation(pProgram, name);
		if (loc != -1) {
			glGetActiveUniformsiv(pProgram, 1, (GLuint*)&i, GL_UNIFORM_ARRAY_STRIDE, &uniArrayStride);
			addUniform(name, type, size);
		}
	}
	free(name);
}


void
VSShaderLib::addUniform(std::string name, GLenum type, unsigned int size) {

	myUniforms u;
	u.type = type;
	u.location =  glGetUniformLocation(pProgram, name.c_str());
	u.size = size;
	pUniforms[name] = u;
}


int 
VSShaderLib::typeSize(int type) {

	int s;

	switch(type) {
	
		case GL_FLOAT: 
			s = sizeof(float);
			break;
		case GL_FLOAT_VEC2:  
			s = sizeof(float)*2;
			break;
		case GL_FLOAT_VEC3:  
			s = sizeof(float)*3;
			break;
		case GL_FLOAT_VEC4:  
			s = sizeof(float)*4;
			break;

#ifndef __ANDROID_API__
		// Doubles
		case GL_DOUBLE: 
			s = sizeof(double);
			break;
		case GL_DOUBLE_VEC2:  
			s = sizeof(double) * 2;
			break;
		case GL_DOUBLE_VEC3:  
			s = sizeof(double) * 3;
			break;
		case GL_DOUBLE_VEC4:  
			s = sizeof(double) * 4;
			break;
#endif
		// Samplers, Ints and Bools
#ifndef __ANDROID_API__
		case GL_IMAGE_1D :
		case GL_IMAGE_2D_RECT :
		case GL_IMAGE_BUFFER :
		case GL_IMAGE_1D_ARRAY :
		case GL_IMAGE_CUBE_MAP_ARRAY :
		case GL_IMAGE_2D_MULTISAMPLE :
		case GL_IMAGE_2D_MULTISAMPLE_ARRAY :
		case GL_INT_IMAGE_1D :
		case GL_INT_IMAGE_2D_RECT :
		case GL_INT_IMAGE_BUFFER :
		case GL_INT_IMAGE_1D_ARRAY :
		case GL_INT_IMAGE_CUBE_MAP_ARRAY :
		case GL_INT_IMAGE_2D_MULTISAMPLE :
		case GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY :
		case GL_UNSIGNED_INT_IMAGE_1D :
		case GL_UNSIGNED_INT_IMAGE_2D_RECT :
		case GL_UNSIGNED_INT_IMAGE_BUFFER :
		case GL_UNSIGNED_INT_IMAGE_1D_ARRAY :
		case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY :
		case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE :
		case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY :
#endif
		case GL_IMAGE_2D :
		case GL_IMAGE_3D :
		case GL_IMAGE_CUBE :
		case GL_IMAGE_2D_ARRAY :
		case GL_INT_IMAGE_2D :
		case GL_INT_IMAGE_3D :
		case GL_INT_IMAGE_CUBE :
		case GL_INT_IMAGE_2D_ARRAY :
		case GL_UNSIGNED_INT_IMAGE_2D :
		case GL_UNSIGNED_INT_IMAGE_3D :
		case GL_UNSIGNED_INT_IMAGE_CUBE :
		case GL_UNSIGNED_INT_IMAGE_2D_ARRAY :

#ifndef __ANDROID_API__
			case GL_SAMPLER_1D:
		case GL_SAMPLER_1D_SHADOW:
		case GL_SAMPLER_1D_ARRAY:
		case GL_SAMPLER_1D_ARRAY_SHADOW:
		case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
		case GL_SAMPLER_BUFFER:
		case GL_SAMPLER_2D_RECT:
		case GL_SAMPLER_2D_RECT_SHADOW:
		case GL_INT_SAMPLER_1D:
		case GL_INT_SAMPLER_1D_ARRAY:
		case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
		case GL_INT_SAMPLER_BUFFER:
		case GL_INT_SAMPLER_2D_RECT:
		case GL_UNSIGNED_INT_SAMPLER_1D:
		case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
		case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
		case GL_UNSIGNED_INT_SAMPLER_BUFFER:
		case GL_UNSIGNED_INT_SAMPLER_2D_RECT:
#endif
		case GL_SAMPLER_2D:
		case GL_SAMPLER_3D:
		case GL_SAMPLER_CUBE:
		case GL_SAMPLER_2D_SHADOW:
		case GL_SAMPLER_2D_ARRAY:
		case GL_SAMPLER_2D_ARRAY_SHADOW:
		case GL_SAMPLER_2D_MULTISAMPLE:
		case GL_SAMPLER_CUBE_SHADOW:
		case GL_INT_SAMPLER_2D:
		case GL_INT_SAMPLER_3D:
		case GL_INT_SAMPLER_CUBE:
		case GL_INT_SAMPLER_2D_ARRAY:
		case GL_INT_SAMPLER_2D_MULTISAMPLE:
		case GL_UNSIGNED_INT_SAMPLER_2D:
		case GL_UNSIGNED_INT_SAMPLER_3D:
		case GL_UNSIGNED_INT_SAMPLER_CUBE:
		case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
		case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
		case GL_BOOL:
		case GL_INT :
			s = sizeof(int);
			break;
		case GL_BOOL_VEC2:
		case GL_INT_VEC2:  
			s = sizeof(int) * 2;
			break;
		case GL_BOOL_VEC3:
		case GL_INT_VEC3:  
			s = sizeof(int) * 3;
			break;
		case GL_BOOL_VEC4:
		case GL_INT_VEC4:  
			s = sizeof(int) * 4;
			break;

		// Unsigned ints
		case GL_UNSIGNED_INT: 
			s = sizeof(unsigned int);
			break;
		case GL_UNSIGNED_INT_VEC2:  
			s = sizeof(unsigned int) * 2;
			break;
		case GL_UNSIGNED_INT_VEC3:  
			s = sizeof(unsigned int) * 3;
			break;
		case GL_UNSIGNED_INT_VEC4:  
			s = sizeof(unsigned int) * 4;
			break;

		// Float Matrices
		case GL_FLOAT_MAT2:
			s = sizeof(float) * 4;
			break;
		case GL_FLOAT_MAT3:
			s = sizeof(float) * 9;
			break;
		case GL_FLOAT_MAT4:
			s = sizeof(float) * 16;
			break;
		case GL_FLOAT_MAT2x3:
			s = sizeof(float) * 6;
			break;
		case GL_FLOAT_MAT2x4:
			s = sizeof(float) * 8;
			break;
		case GL_FLOAT_MAT3x2:
			s = sizeof(float) * 6;
			break;
		case GL_FLOAT_MAT3x4:
			s = sizeof(float) * 12;
			break;
		case GL_FLOAT_MAT4x2:
			s = sizeof(float) * 8;
			break;
		case GL_FLOAT_MAT4x3:
			s = sizeof(float) * 12;
			break;
#ifndef __ANDROID_API__
		// Double Matrices
		case GL_DOUBLE_MAT2:
			s = sizeof(double) * 4;
			break;
		case GL_DOUBLE_MAT3:
			s = sizeof(double) * 9;
			break;
		case GL_DOUBLE_MAT4:
			s = sizeof(double) * 16;
			break;
		case GL_DOUBLE_MAT2x3:
			s = sizeof(double) * 6;
			break;
		case GL_DOUBLE_MAT2x4:
			s = sizeof(double) * 8;
			break;
		case GL_DOUBLE_MAT3x2:
			s = sizeof(double) * 6;
			break;
		case GL_DOUBLE_MAT3x4:
			s = sizeof(double) * 12;
			break;
		case GL_DOUBLE_MAT4x2:
			s = sizeof(double) * 8;
			break;
		case GL_DOUBLE_MAT4x3:
			s = sizeof(double) * 12;
			break;
#endif
		default: return 0;
	}
	return s;
}

